﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Text;

using ColladaDotNet.OpenTKMapping;

using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;

using OpenTK;
using OpenTK.Graphics;

namespace Dae2Png
{
	class Program
	{
		static int width = 256;
		static int height = 512;

		static int FBOHandle;
		static int ColorTexture;
		static int DepthTexture;

		static void Main(string[] args)
		{

			string srcFileName = @"D:\GLoom\GLebedev\ColladaDotNet\colladadotnet\trunk\bin\pipeline\mix\head.DAE";
			string dstFileName = null;
			string animation = null;
			string camera = null;//"#node-CameraX_";

			for (int i = 0; i < args.Length; i++)
			{
				var arg = args[i];
				if (arg[0] == '-' || arg[0] == '/')
				{
					switch (arg.Substring(1).ToLower())
					{
						case "i":
						case "input":
							srcFileName = args[i + 1];
							++i;
							break;
						case "o":
						case "output":
							dstFileName = args[i + 1];
							++i;
							break;
						case "c":
						case "camera":
							camera = args[i + 1];
							++i;
							break;
						case "a":
						case "animation":
							animation = args[i + 1];
							++i;
							break;
						case "w":
						case "width":
							width = int.Parse(args[i + 1]);
							++i;
							break;
						case "h":
						case "height":
							height = int.Parse(args[i + 1]);
							++i;
							break;
					}
				}
			}
			Document doc = new Document();
			if (string.IsNullOrEmpty(dstFileName))
			{
				dstFileName = Path.GetFileNameWithoutExtension(srcFileName) + ".png";
			}
			doc.Load(srcFileName);

			var hidden = new GameWindow(width, height, GraphicsMode.Default, "OpenTK | Hidden input window", GameWindowFlags.Default, DisplayDevice.Default);
			//hidden.Visible = false;
			//hidden.Load += hidden_Load;
			//hidden.Unload += hidden_Unload;
			//hidden.RenderFrame += hidden_RenderFrame;
			//hidden.Run(60.0, 0.0);


			var scene = doc.GetAnyScene();

			Node cameraNode;
			if (!string.IsNullOrEmpty(camera))
				scene.ActiveCamera = doc.GetElementByUrl<Node>(camera);
			

			Animation a = null;
			if (!string.IsNullOrEmpty(animation))
				a = doc.GetElementByUrl<Animation>(animation);
			else if (doc.Animations.Count > 0)
				a = doc.Animations[0];

			scene.Animation = a;

			InitOpenGL();

			if (a != null)
			{
				for (int i = 0; i < a.GetFrames().Length; i++)
				{
					var d = a.GetFrames()[i];
					RenderFrame(Path.ChangeExtension(dstFileName, i + Path.GetExtension(dstFileName)), scene,d);
				}
			}
			else
			{
				RenderFrame(dstFileName, scene,0);
			}


		}

		private static void RenderFrame(string dstFileName, VisualScene scene, double time)
		{
			GL.Viewport(0, 0, width, height);

			// clear the screen in red, to make it very obvious what the clear affected. only the FBO, not the real framebuffer
			GL.ClearColor(0f, 0f, 0f, 0f);
			GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

			scene.Render((double)width / (double)height, time);
			//GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0);

			//GL.ReadBuffer(ReadBufferMode.ColorAttachment0);
			Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
			BitmapData data = bmp.LockBits(
					new Rectangle(0, 0, width, height),
					ImageLockMode.WriteOnly,
					System.Drawing.Imaging.PixelFormat.Format32bppArgb);
			GL.ReadPixels(0, 0, width, height, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
			bmp.UnlockBits(data);
			bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
			bmp.Save(dstFileName);
		}

		private static void InitOpenGL()
		{
			// Create Color Tex
			GL.GenTextures(1, out ColorTexture);
			GL.BindTexture(TextureTarget.Texture2D, ColorTexture);
			GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8, width, height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
			GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
			GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
			GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToBorder);
			GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToBorder);
			// GL.Ext.GenerateMipmap( GenerateMipmapTarget.Texture2D );

			// Create Depth Tex
			GL.GenTextures(1, out DepthTexture);
			GL.BindTexture(TextureTarget.Texture2D, DepthTexture);
			GL.TexImage2D(TextureTarget.Texture2D, 0, (PixelInternalFormat)All.DepthComponent32, width, height, 0, OpenTK.Graphics.OpenGL.PixelFormat.DepthComponent, PixelType.UnsignedInt, IntPtr.Zero);
			// things go horribly wrong if DepthComponent's Bitcount does not match the main Framebuffer's Depth
			GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
			GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
			GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToBorder);
			GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToBorder);
			// GL.Ext.GenerateMipmap( GenerateMipmapTarget.Texture2D );


			GL.Ext.GenFramebuffers(1, out FBOHandle);
			GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, FBOHandle);
			GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt, FramebufferAttachment.ColorAttachment0Ext, TextureTarget.Texture2D, ColorTexture, 0);
			GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt, FramebufferAttachment.DepthAttachmentExt, TextureTarget.Texture2D, DepthTexture, 0);

			GL.Enable(EnableCap.CullFace);
			GL.Enable(EnableCap.DepthTest);
		}

		static Random rnd = new Random();
		public const float scale = 0.3f;

		/// <summary>Returns a random Float in the range [-0.5*scale..+0.5*scale]</summary>
		public static float GetRandom()
		{
			return (float)(rnd.NextDouble() - 0.5) * scale;
		}

		/// <summary>Returns a random Float in the range [0..1]</summary>
		public static float GetRandom0to1()
		{
			return (float)rnd.NextDouble();
		}

	}
}
